package com.accedo.achievements.dataaccess.dao.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import com.accedo.achievements.dataaccess.dao.BadgeDao;
import com.accedo.achievements.dataaccess.model.Badge;

/**
 * A DAO class with badge model database operations
 * @author Bo Liang
 */
@Component
public class BadgeDaoImpl implements BadgeDao{

	private static final Logger log = LoggerFactory.getLogger(BadgeDaoImpl.class);
	
	private SessionFactory sessionFactory; //injection
	private Badge badge; //injection
	
	// Constructor
	BadgeDaoImpl () {};

	/**
	 * Get badge object by using this badge's serial number.
	 * @param badge_sn	badge serial number
	 * @return			badge object
	 */
	public Badge getBadgeByBadgesn(String badge_sn) {
		log.debug("Entering getBadgeByBadgesn(badge_sn = {})", badge_sn);
		Session session = null; 
		try {
//			session = SessionFactoryUtil.getSessionFactory().openSession(); 
			session = sessionFactory.openSession();
			Query query = session.createQuery("from Badge badge where badge.badge_sn = :badge_sn")
					.setParameter("badge_sn", badge_sn); 
			badge = (Badge)query.uniqueResult();  
		} catch (Exception e) {
			e.printStackTrace();
			log.error("getBadgeByBadgesn Catch Exception: {}", e.getMessage());
		} finally {
//			SessionFactoryUtil.closeSession(session);
			session.close();
		}
		log.debug("Leaving getBadgeByBadgesn(): badge_name = {}", badge.getBadge_name());
		return badge;  
	}
	
	/**
	 * Get badge id List by using achievement id.
	 * <p> one achievement may related several badges,
	 * it means one badge includes several achievements, 
	 * if user want to obtain this badge, they have to unlock all of the relevant achievements.
	 * And several badges may have one same achievement,
	 * this function returns these badges which have this same achievement,
	 * return these badge's id in a List
	 * 
	 * @param achi_id	achievement id
	 * @return			List of badge's id
	 */
	@SuppressWarnings("unchecked")
	public List<Integer> getBadgeIdListByAchiId(int achi_id) {
		log.debug("Entering getBadgeIdListByAchiId(achi_id = {})", achi_id);
		List<Integer> badgeIdList = new ArrayList<Integer>();
		Session session = null;
		try {
//			session = SessionFactoryUtil.getSessionFactory().openSession(); 
			session = sessionFactory.openSession();
			Query query = session.createSQLQuery("select badge_id from t_badge_achievement where achi_id = :achi_id")
					.setParameter("achi_id", achi_id); 
			badgeIdList = query.list(); 
		} catch (Exception e) {
			e.printStackTrace();
			log.error("getBadgeIdListByAchiId Catch Exception: {}", e.getMessage());
		} finally {
//			SessionFactoryUtil.closeSession(session);
			session.close();
		}
		log.debug("Leaving getBadgeIdListByAchiId(): badgeIdList.size = {}", badgeIdList.size());
		return badgeIdList; 
	}
	
	/**
	 * Get all badge objects from the database.
	 * @return List of badge object
	 */
	@SuppressWarnings("unchecked")
	public List<Badge> getBadgeList() {
		log.debug("Entering getBadgeList()");
		List<Badge> badgeList = new ArrayList<Badge>(); 
		Session session = null;
		try {
//			session = SessionFactoryUtil.getSessionFactory().openSession(); 
			session = sessionFactory.openSession(); 
			Query query = session.createQuery("from Badge badge"); 
			badgeList = query.list();
		
		} catch (Exception e) {
			e.printStackTrace();
			log.error("getBadgeList Catch Exception: {}", e.getMessage());
		} finally {
//			SessionFactoryUtil.closeSession(session);
			session.close();
		}
		log.debug("Leaving getBadgeList(): badgeList.size = {}", badgeList.size());
		return badgeList;
	}
	
	/**
	 * Get the user's all active badge objects in Map format by using user id.
	 * <p>the active badge for user means that this user at least unlocked one achievement related this badge.
	 * this function return two types of Map key-value pair: if one badge's achievements have already totally unlocked by user, it puts <badge_id, "finished">,
	 * if one badge's achievements have not unlocked completed but at least one, it puts <badge_id, "playing"> into Map.
	 * @param user_id	user id
	 * @return			List of badge id
	 */
	@SuppressWarnings("unchecked")
	public Map<Integer, String> getActiveBadgeIdMapByUserId(int user_id) {
		log.debug("Entering getActiveBadgeIdMapByUserId(user_id = {})", user_id);
		
		// the badge id list that stored the badge which user have already obtained
		List<Integer> unlockedBadgeIdList = new ArrayList<Integer>();
		
		// the badge id list that stored the badge which user unlocked at least one achievement
		List<Integer> badgeIdList = new ArrayList<Integer>();
		
		Map<Integer, String> returnMap = new HashMap<Integer, String>();
		int badge_id;
		Session session = null; 
		
		try {
//			session = SessionFactoryUtil.getSessionFactory().openSession(); 
			session = sessionFactory.openSession();
			
			// unlock_flag equals 1 means this badge's achievements totally unlocked and this user obtained this badge
			Query query = session.createSQLQuery("select badge_id from t_user_badge where user_id = :user_id and unlock_flag = 1")
					.setParameter("user_id", user_id); 
			unlockedBadgeIdList = query.list();  
			
			query = session.createSQLQuery("select badge_id from t_user_badge where user_id = :user_id")
					.setParameter("user_id", user_id); 
			badgeIdList = query.list();
			
			for (Iterator<Integer> iter = badgeIdList.iterator(); iter.hasNext(); ) {
				badge_id = iter.next(); 
				if (unlockedBadgeIdList.contains(badge_id)) {
					// if this badge is obtained, put "finished" in the key-value pair
					returnMap.put(badge_id, "finished");
				} else {
					/**
					 *  if this badge has at least one achievement unlocked but not completed all of achievement, 
					 *  put "playing" in the key-value pair
					 */
					returnMap.put(badge_id, "playing");
				}
			}	
		} catch (Exception e) {
			e.printStackTrace();
			log.error("getActiveBadgeIdMapByUserId Catch Exception: {}", e.getMessage());
		} finally {
//			SessionFactoryUtil.closeSession(session);
			session.close();
		}
		log.debug("Leaving getActiveBadgeIdMapByUserId(): returnMap.size = {}", returnMap.size());
		return returnMap; 
	}

	public SessionFactory getSessionFactory() {
		return sessionFactory;
	}

	@Resource
	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}

	public Badge getBadge() {
		return badge;
	}

	@Resource
	public void setBadge(Badge badge) {
		this.badge = badge;
	}	
}
